<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.9.1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Arduino A2DP: A Simple Arduino Bluetooth Music Receiver and Sender for the ESP32</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td id="projectalign" style="padding-left: 0.5em;">
   <div id="projectname">Arduino A2DP
   </div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.9.1 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(function() {
  initMenu('',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
/* @license-end */</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

</div><!-- top -->
<div class="PageDoc"><div class="header">
  <div class="headertitle">
<div class="title">A Simple Arduino Bluetooth Music Receiver and Sender for the ESP32 </div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p>The ESP32 provides a Bluetooth A2DP API that receives sound data e.g. from your Mobile Phone and makes it available via a callback method. The output is a PCM data stream decoded from SBC format. The documentation can be found <a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2s.html">here</a>.</p>
<p>I2S is an electrical serial bus interface standard used for connecting digital audio devices together. It is used to communicate PCM audio data between integrated circuits in an electronic device.</p>
<p>So we can just feed the input from Bluetooth to the I2S output: An example for this from Expressive can be found on <a href="https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/classic_bt/a2dp_sink">Github</a>.</p>
<p>Unfortunately this example did not make me happy so I decided to convert it into a simple <b>Arduino Library</b> that is very easy to use from an Arduino Software IDE.</p>
<h1><a class="anchor" id="autotoc_md1"></a>
A2DP Sink</h1>
<h2><a class="anchor" id="autotoc_md2"></a>
A Simple I2S Example (A2DS Sink)</h2>
<p>Here is the simplest example which just uses the proper default settings:</p>
<div class="fragment"><div class="line">#include &quot;BluetoothA2DPSink.h&quot;</div>
<div class="line"> </div>
<div class="line">BluetoothA2DPSink a2dp_sink;</div>
<div class="line"> </div>
<div class="line">void setup() {</div>
<div class="line">    a2dp_sink.start(&quot;MyMusic&quot;);</div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">void loop() {</div>
<div class="line">}</div>
</div><!-- fragment --><p> This creates a new Bluetooth device with the name “MyMusic” and the output will be sent to the following default I2S pins: – bck_io_num = 26, – ws_io_num = 25, – data_out_num = 22,</p>
<p>which need to be conected to an external DAC. You can define your own pins easily by calling the set_pin_config method.</p>
<h2><a class="anchor" id="autotoc_md3"></a>
Output to the Internal DAC</h2>
<p>You can also send the output directly to the internal DAC of the ESP32 by providing the corresponding i2s_config:</p>
<div class="fragment"><div class="line">#include &quot;BluetoothA2DPSink.h&quot;</div>
<div class="line"> </div>
<div class="line">BluetoothA2DPSink a2dp_sink;</div>
<div class="line"> </div>
<div class="line">void setup() {</div>
<div class="line">    static const i2s_config_t i2s_config = {</div>
<div class="line">        .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),</div>
<div class="line">        .sample_rate = 44100, // corrected by info from bluetooth</div>
<div class="line">        .bits_per_sample = (i2s_bits_per_sample_t) 16, /* the DAC module will only take the 8bits from MSB */</div>
<div class="line">        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,</div>
<div class="line">        .communication_format = I2S_COMM_FORMAT_I2S_MSB,</div>
<div class="line">        .intr_alloc_flags = 0, // default interrupt priority</div>
<div class="line">        .dma_buf_count = 8,</div>
<div class="line">        .dma_buf_len = 64,</div>
<div class="line">        .use_apll = false</div>
<div class="line">    };</div>
<div class="line"> </div>
<div class="line">    a2dp_sink.set_i2s_config(i2s_config);</div>
<div class="line">    a2dp_sink.start(&quot;MyMusic&quot;);</div>
<div class="line"> </div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">void loop() {</div>
<div class="line">}</div>
</div><!-- fragment --><p>The output goes now to the DAC pins G26/G27.</p>
<h2><a class="anchor" id="autotoc_md4"></a>
Accessing the Sink Data Stream with Callbacks</h2>
<p>You can be notified when a packet is received:</p>
<div class="fragment"><div class="line">// In the setup function:</div>
<div class="line">a2dp_sink.set_on_data_received(data_received_callback);</div>
<div class="line"> </div>
<div class="line"> </div>
<div class="line">// Then somewhere in your sketch:</div>
<div class="line">void data_received_callback() {</div>
<div class="line">  Serial.println(&quot;Data packet received&quot;);</div>
<div class="line">}</div>
</div><!-- fragment --><p>Or you can access the packet:</p>
<div class="fragment"><div class="line">// In the setup function:</div>
<div class="line">a2dp_sink.set_stream_reader(read_data_stream);</div>
<div class="line"> </div>
<div class="line">// Then somewhere in your sketch:</div>
<div class="line">void read_data_stream(const uint8_t *data, uint32_t length)</div>
<div class="line">{</div>
<div class="line">  // Do something with the data packet</div>
<div class="line">}</div>
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md5"></a>
Support for Metadata</h2>
<p>You can register a method which will be called when the system receives any AVRC metadata. Here is an example </p><div class="fragment"><div class="line">void avrc_metadata_callback(uint8_t data1, const uint8_t *data2) {</div>
<div class="line">  Serial.printf(&quot;AVRC metadata rsp: attribute id 0x%x, %s\n&quot;, data1, data2);</div>
<div class="line">}</div>
<div class="line">a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);</div>
<div class="line">a2dp_sink.start(&quot;BT&quot;);</div>
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md6"></a>
Support for AVRC Commands</h2>
<p>I have added the following AVRC commmands, that you can use to 'control' your A2DP Source:</p>
<ul>
<li>play();</li>
<li>pause();</li>
<li>stop();</li>
<li>next();</li>
<li>previous();</li>
</ul>
<h1><a class="anchor" id="autotoc_md7"></a>
A2DP Source</h1>
<h2><a class="anchor" id="autotoc_md8"></a>
Sending Data from a A2DS Data Source with a Callback</h2>
<p>We can also generate sound and send it e.g. to a Bluetooth Speaker. <br  />
</p>
<p>The supported audio codec in ESP32 A2DP is SBC: A SBC audio stream is encoded from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data.</p>
<p>When you start the <a class="el" href="class_bluetooth_a2_d_p_source.html" title="A2DP Bluetooth Source.">BluetoothA2DPSource</a>, you need to pass the Bluetooth name that you want to connect to and a 'call back function' that generates the sound data:</p>
<div class="fragment"><div class="line">#include &quot;BluetoothA2DPSource.h&quot;</div>
<div class="line"> </div>
<div class="line">BluetoothA2DPSource a2dp_source;</div>
<div class="line"> </div>
<div class="line">// callback </div>
<div class="line">int32_t get_sound_data(Channels *data, int32_t len) {</div>
<div class="line">    // generate your sound data </div>
<div class="line">    // return the length of the generated sound - which usually is identical with len</div>
<div class="line">    return len;</div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">void setup() {</div>
<div class="line">  a2dp_source.start(&quot;MyMusic&quot;, get_sound_data);  </div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">void loop() {</div>
<div class="line">}</div>
</div><!-- fragment --><p>In the examples you can find an impelentation that generates sound with the help of the sin() function. You can also inticate multiple alternative Bluetooth names. The system just connects to the first one which is available:</p>
<div class="fragment"><div class="line">void setup() {</div>
<div class="line">  static std::vector&lt;char*&gt; bt_names = {&quot;MyMusic&quot;,&quot;RadioPlayer&quot;,&quot;MusicPlayer&quot;};</div>
<div class="line">  a2dp_source.start(bt_names, get_sound_data); </div>
<div class="line">} </div>
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md9"></a>
Sending Data from a A2DS Data Source with Recorded Data</h2>
<p>You can also provide the data directly as simple array of uint8_t:</p>
<div class="fragment"><div class="line">#include &quot;BluetoothA2DPSource.h&quot;</div>
<div class="line"> </div>
<div class="line">extern const uint8_t StarWars10_raw[];</div>
<div class="line">extern const unsigned int StarWars10_raw_len;</div>
<div class="line"> </div>
<div class="line">BluetoothA2DPSource a2dp_source;</div>
<div class="line">SoundData *music = new OneChannelSoundData((int16_t*)StarWars30_raw, StarWars30_raw_len/2);</div>
<div class="line"> </div>
<div class="line">void setup() {</div>
<div class="line">  a2dp_source.start(&quot;RadioPlayer&quot;);  </div>
<div class="line">  a2dp_source.writeData(music);</div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">void loop() {</div>
<div class="line">}</div>
</div><!-- fragment --><p>The array can be prepared e.g. in the following way:</p>
<ul>
<li>Open any sound file in Audacity.<ul>
<li>Select Tracks -&gt; Resample and select 44100</li>
<li>Export -&gt; Export Audio -&gt; Header Raw ; Signed 16 bit PCM</li>
</ul>
</li>
<li>Convert to c file e.g. with "xxd -i file_example_WAV_1MG.raw file_example_WAV_1MG.c"<ul>
<li>add the const qualifier to the generated array definition. E.g const unsigned char file_example_WAV_1MG_raw[] = {</li>
</ul>
</li>
</ul>
<p>You might want to compile with the Partition Scheme: Huge App!</p>
<p>In the example above we provide the data with one channel. This has the advantage that it uses much less space then a 2 channel recording, which you could use in the following way:</p>
<div class="fragment"><div class="line">SoundData *data = new TwoChannelSoundData((Channels*)StarWars10_raw,StarWars10_raw_len/4);</div>
</div><!-- fragment --><p>In the constructor you can pass additional parameters:</p>
<div class="fragment"><div class="line">TwoChannelSoundData(Channels *data, int32_t len, bool loop=false);</div>
<div class="line">OneChannelSoundData(int16_t *data, int32_t len, bool loop=false, ChannelInfo channelInfo=Both);</div>
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md10"></a>
Installation</h1>
<p>You can download the library as zip and call include Library -&gt; zip library. Or you can git clone this project into the Arduino libraries folder e.g. with </p><div class="fragment"><div class="line">cd  ~/Documents/Arduino/libraries</div>
<div class="line">git clone pschatzmann/ESP32-A2DP.git</div>
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md11"></a>
Change History</h1>
<p>V.1.2.0</p><ul>
<li>Metadata support with the help of a callback function - Thanks to <a href="https://github.com/JohnyMielony">JohnyMielony</a></li>
<li>AVRC command support thanks to <a href="https://github.com/KIdon-Park">PeterPark</a></li>
<li>Improved init_bluetooth checks, in case bluedroid was already initialized elsewhere - Thanks to <a href="https://github.com/ant0nisk">Antonis Katzourakis</a></li>
<li>No auto reconnect after clean disconnect - Thanks to <a href="https://github.com/leventeBajczi">Bajczi Levente</a></li>
<li>The data is rescaled to when written to the internal DAC - Thanks to <a href="https://github.com/thinkcz">Martin Hron</a></li>
<li>Corrected wrong case of include to Arduino.h - Thanks to <a href="https://github.com/RyanDavis">RyanDavis</a></li>
<li>Added callback to received packets - Thanks to <a href="https://github.com/Mishaux">Mishaux</a></li>
<li>Automatically reconnect to last source - Thanks to <a href="https://github.com/JohnyMielony">JohnyMielony</a></li>
<li>Support for data callback - Thanks to <a href="https://github.com/Mishaux">Mike Mishaux</a></li>
<li>Improved init_bluetooth checks, in case bluedroid was already initialized elsewhere <a href="https://github.com/ant0nisk">Antonis Katzourakis</a></li>
<li>No auto reconnect after clean disconnect thanks to <a href="https://github.com/leventeBajczi">Bajczi Levente</a></li>
<li>Made all methods virtual to enable flexible subclassing</li>
<li>Error Corrections in <a class="el" href="class_bluetooth_a2_d_p_source.html" title="A2DP Bluetooth Source.">BluetoothA2DPSource</a></li>
<li>Support for writeData in <a class="el" href="class_bluetooth_a2_d_p_source.html" title="A2DP Bluetooth Source.">BluetoothA2DPSource</a></li>
<li>Support for multiple alternative BT names in <a class="el" href="class_bluetooth_a2_d_p_source.html" title="A2DP Bluetooth Source.">BluetoothA2DPSource</a></li>
</ul>
<p>V.1.1.0</p><ul>
<li>New functionality: <a class="el" href="class_bluetooth_a2_d_p_source.html" title="A2DP Bluetooth Source.">BluetoothA2DPSource</a></li>
<li>Renamed project from 'esp32_bt_music_receiver' to 'ESP32-A2DP'</li>
<li>Corrected Spelling Mistake from Blootooth to Bluetooth in Class names: The correct class name is BluetoothA2DPSink!</li>
<li>The include h files are now called like the class names (e.g. <a class="el" href="_bluetooth_a2_d_p_sink_8h_source.html">BluetoothA2DPSink.h</a> and <a class="el" href="_bluetooth_a2_d_p_source_8h_source.html">BluetoothA2DPSource.h</a>)</li>
</ul>
<p>V.1.0.0</p><ul>
<li>Initial Release </li>
</ul>
</div></div><!-- contents -->
</div><!-- PageDoc -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.1
</small></address>
</body>
</html>
